# -*-coding: utf-8 -*-
"""
作者：TianyiFan
日期：2023-年11-月07日
用途：调用docplex 求解数学模型
"""
import math

import numpy as np
from docplex.mp.model import Model
from docplex.mp.conflict_refiner import ConflictRefiner
from swo_algo_method import solution_record
import para as params
import time
import random as rd
rd.seed(params.seed)

class docplex_model:
    def __init__(self):
        # print("\nCplex_model 中params.Pair_N = {}".format(params.pair_new))
        self.optimal_rd = solution_record()
        self.solving_time = 0
        self.solving_time_lb = 0
        self.solving_time_compare = 0
        self.obj_lb = params.max_bound
        self.obj =  params.max_bound
        self.obj_compare = params.max_bound
        self.lb_cost_i = np.zeros(params.V+1,dtype=float)
        self.solution_gamma_ik = {i:[] for i in range(params.V+1)}
        self.cplex_output = []     # 原模型时间构成
        self.compare_output = []   # 对比模型时间构成

    def build_model(self,model_name,verify_gamma_ik = None,problem_scale = 3):
        time_start = time.time()
        maxbound = 999999
        minbound = -999999
        # region 定义模型
        model = Model()
        if problem_scale <= 0:
            model.set_time_limit(params.test_running_limitation)  # 测试规模最多求300s
        elif problem_scale <= 1:
            limit_time = rd.randint(1200, 2400)
            model.set_time_limit(limit_time)   # 测试1/2问题规模最多求1200-2400s
        else:
            model.set_time_limit(3600)         # 正式规模最多求3600s
        # region 定义决策变量
        alpha_uih = {}
        alpha_lih = {}
        beta_lik = {}
        beta_uik = {}
        ganma_ik = {}
        for i in range(params.V + 2):
            for h in range(params.H):
                name1 = "alpha_uij_{0:>2}_{1}_".format(i, h)
                name2 = "alpha_lij_{0:>2}_{1}_".format(i, h)
                alpha_uih[i, h] = model.integer_var(0, params.c_ui[i], name=name1)
                alpha_lih[i, h] = model.integer_var(0, params.c_li[i], name=name2)
        for i in range(params.V + 2):
            for k in range(params.K1):
                name3 = "beta_l{0}_{1}".format(i, k)
                name4 = "beta_u{0}_{1}".format(i, k)
                name5 = "ganma_{0}_{1}".format(i, k)
                beta_lik[i, k] = model.integer_var(0, params.z,name=name3)
                beta_uik[i, k] = model.integer_var(0, params.z,name=name4)
                ganma_ik[i, k] = model.binary_var(name=name5)

        # 辅助决策变量用于线性化
        deta_u_i = {}
        deta_l_i = {}
        abs_ui1 = {}
        abs_li1 = {}
        sum_li1 = {}
        sum_ui1 = {}
        for i in range(1, params.V + 1):
            name6 = "deta_u_{0}".format(i)
            name7 = "deta_l_{0}".format(i)
            name8 = "abs_u_{0}".format(i)
            name9 = "abs_l_{0}".format(i)
            name10 = "sum_u_{0}".format(i)
            name11 = "sum_i_{0}".format(i)
            deta_u_i[i] = model.integer_var(0, maxbound, name=name6)
            deta_l_i[i] = model.integer_var(0, maxbound, name=name7)
            abs_ui1[i] = model.integer_var(0, maxbound, name=name8)
            abs_li1[i] = model.integer_var(0, maxbound, name=name9)
            sum_ui1[i] = model.integer_var(minbound, maxbound, name=name10)
            sum_li1[i] = model.integer_var(minbound, maxbound, name=name11)
        # endregion

        # region 定义目标函数
        obj_expr = model.linear_expr()  # 创建一个空的线性表达式对象
        for i in range(1, params.V + 1):
            expr1 = model.linear_expr()  # 创建一个空的线性表达式对象
            expr1.add_term(deta_l_i[i],params.time_p)
            expr1.add_term(deta_u_i[i], params.time_p)
            expr1.add_term(abs_li1[i],params.time_u)
            expr1.add_term(abs_ui1[i],params.time_u)
            expr2 = model.sum(beta_lik[i,k]*params.t_lki[k,i] for k in range(params.K1))
            expr3 = model.sum(beta_uik[i, k] * params.t_uki[k, i] for k in range(params.K1))
            obj_expr.add(expr1)
            obj_expr.add(expr2)
            obj_expr.add(expr3)
            if params.add_cost:
                expr4 = model.sum(ganma_ik[i, k] * params.t_subblock for k in range(params.K1))
                obj_expr.add(expr4)
        model.minimize(obj_expr)
        # endregion

        # 验证算法得到的解，是否合理：
        if verify_gamma_ik is not None:
            for i in range(1,params.V+1):
                for k in range(params.K1):
                    model.add(verify_gamma_ik[i,k] == ganma_ik[i,k], name="verify_gamma_{}_{}".format(i,k))

        # region 定义约束条件
        # 目标函数中的等价替换
        for i in range(1, params.V + 1):
            for h in range(params.H):
                model.add(deta_l_i[i] >= alpha_lih[i, h], name="linearing cons1_{0}_{1}".format(i, h))
                model.add(deta_u_i[i] >= alpha_uih[i, h], name="linearing cons2_{0}_{1}".format(i, h))
        for i in range(1, params.V + 1):
            expr3 = model.sum(beta_lik[i, k] for k in range(params.K0))
            expr4 = model.sum(beta_uik[i, k] for k in range(params.K0))
            model.add(sum_li1[i] == alpha_lih[i, 0] - expr3, name="Constrain_sum_l_{0}".format(i))
            model.add(sum_ui1[i] == alpha_uih[i, 0] - expr4, name="Constrain_sum_u_{0}".format(i))
        for i in range(1, params.V + 1):
            model.add(abs_li1[i] == model.abs(sum_li1[i]), name="abs_constrain_l_{0}".format(i))
            model.add(abs_ui1[i] == model.abs(sum_ui1[i]), name="abs_constrain_u_{0}".format(i))
        # 约束2/4：每艘船的unloading量守恒、loading量守恒
        if 1 == 1:
            for i in range(1, params.V + 1):
                expr1 = model.sum(alpha_uih[i,h] for h in range(params.H))
                expr2 = model.sum(alpha_lih[i,h] for h in range(params.H))
                model.add(expr1 == params.c_ui[i], name="Constrain2_u_{0}".format(i))
                model.add(expr2 == params.c_li[i], name="Constrain4_l_{0}".format(i))
        # 约束 3/5：每艘船unloading/loading到堆场的数量守恒
        if 1 == 1:
            for i in range(1, params.V + 1):
                expr3 = model.sum(beta_uik[i, k] for k in range(params.K1))
                expr4 = model.sum(beta_lik[i, k] for k in range(params.K1))
                model.add(expr3 == params.c_ui[i], name="Constrain2_u_{0}".format(i))
                model.add(expr4 == params.c_li[i], name="Constrain4_l_{0}".format(i))
        # 约束6：unloading 和 loading 在每一层的上界
        if 1 == 1:
            for i in range(1, params.V + 1):
                for k in range(params.K1):
                    model.add(beta_lik[i, k] + beta_uik[i, k] <= params.z*ganma_ik[i, k],name="Constrain6_{0}_{1}".format(i, k))

        # 约束9：每层的subblock需要小于对应的params.r_ih
        for i in range(1, params.V + 1):
            expr1 = model.sum(ganma_ik[i,k] for k in range(params.K0))
            expr2 = model.sum(ganma_ik[i, k] for k in range(params.K0, params.K1))
            model.add(expr1 <= params.r_ih[i][0], name="Constrain9_0_{0}".format(i))
            model.add(expr2 <= params.r_ih[i][1], name="Constrain9_1_{0}".format(i))

        # 约束10：每个subblock只能分配给一个堆场
        if 1 == 1:
            for k in range(params.K1):
                expr = model.sum(ganma_ik[i,k] for i in range(1,params.V+1))
                model.add(expr <= 1, name="Constrain10_{0}".format(k))
        # 约束11：每个time step，同一层的同一个block内，分配subblock数量不能超过3
        if 1 == 1:
            for t in range(params.T):
                if params.total_handle[t] >= 1:
                    for g in range(params.G):
                        expr = model.sum(params.l_it[i][t]*ganma_ik[i, k] for i in range(1,params.V+1) for k in params.block_set[g])
                        model.add(expr <= 3, name="Constrain11_{0}_{1}".format(t, g))
        if 1 == 1:
            for t in range(params.T):
                if params.total_handle[t] >= 1:
                    for n in range(params.K1):
                        for k in range(1, len(params.neighbor_set[n])):  # 不包括自己
                            midlist = [n]
                            midlist.append(params.neighbor_set[n][k])
                            expr = model.linear_expr()
                            index_neighbor = n
                            for i in range(1, params.V + 1):
                                for index in midlist:  # --形成一个pair 对 等价于 for k in neighbor_pair
                                    index_neighbor = index
                                    expr.add_term(ganma_ik[i, index],params.l_it[i][t])
                            model.add(expr <= 1, name="Constrain12_{0}_({1}-{2})".format(t, n, index_neighbor))
        if 1 == 1:
            for t in range(params.T):
                expr = model.linear_expr()
                for i in range(1, params.V + 1):
                    if params.l_it[i][t] == 1:
                        expr.add_term(abs_ui1[i] , 1 / params.handle_i[i])
                        expr.add_term(abs_li1[i], 1 / params.handle_i[i])
                model.add(expr <= params.C, name="Constrain13_{0}".format(t))
        # endregion

        solution = model.solve(log_output=True)
        time_end = time.time()
        num_variables = model.number_of_variables
        num_constraints = model.number_of_constraints
        refiner = ConflictRefiner()  # 先实例化ConflictRefiner类
        res = refiner.refine_conflict(model)  # 将模型导入该类，调用方法
        res.display()

        print("{} 中决策变量个数={}  约束数量={}".format(model_name,num_variables,num_constraints))
        if solution is not None:
            model_obj = solution.get_objective_value()
            self.solving_time = round(time_end - time_start, 2)
            self.obj = solution.get_objective_value()

            cost_i = [0 for _ in range(params.V + 1)]
            cost_t = 0
            cost_p = 0
            cost_u = 0
            for i in range(1, params.V + 1):
                cost1 = params.time_p * (max(solution.get_var_value(alpha_lih[i, 0]),solution.get_var_value(alpha_lih[i, 1])) +
                                         max(solution.get_var_value(alpha_uih[i, 0]),solution.get_var_value(alpha_uih[i, 1])))
                # unloading_value = 0
                # loading_Value = 0
                # for k in range(params.K0):
                #     unloading_value += solution.get_var_value(beta_uik[i, k])
                #     loading_Value += solution.get_var_value(beta_lik[i, k])
                # print("abs_ui1 ={}   公式计算的abs_ui1={}".format(abs_ui1[i].x,abs(alpha_uih[i, 0].x - unloading_value)))
                # cost2 = params.time_u * (abs(alpha_lih[i, 0].x - loading_Value) + abs(alpha_uih[i, 0].x - unloading_value))
                cost2222 = params.time_u * (solution.get_var_value(abs_ui1[i])+ solution.get_var_value(abs_li1[i]))
                # print("abs直接计算的结果={}   由原来的公式计算的结果={}".format(cost2222,cost2))
                cost3 = 0
                for k in range(params.K1):
                    cost3 += params.t_lki[k, i] *solution.get_var_value(beta_lik[i, k]) + params.t_uki[k, i] *solution.get_var_value(beta_uik[i, k])
                cost_i[i] = cost1 + cost2222 + cost3
                cost_t+=cost3
                cost_p+=cost1
                cost_u+=cost2222
            # print("每艘船预留堆场的最优解情况-------------")
            # for i in range(1, params.V+ 1):
            #     print("vessel-{}  cost = {:.2f} abs_u={:.2f}  abs_i = {:.2f}  预留subblock = ".
            #           format(i,cost_i[i],solution.get_var_value(abs_ui1[i]),solution.get_var_value(abs_li1[i])), end=" ")
            #     for k in range(params.K1):
            #         if solution.get_var_value(ganma_ik[i, k])> 0.5:
            #             print(f"{k:2} ", end="")
            #     print()
            self.cplex_output.append(cost_t)
            self.cplex_output.append(cost_p)
            self.cplex_output.append(cost_u)
            print("{2} 求解结果： obj = {0:.2f} s  转化为小时={1:.2f} h  sum(vessel)={3:.2f}".format(model_obj, model_obj / 3600, model_name,sum(cost_i)))
        else:
            print("docplex 无解!")

    def build_lower_bound_model(self,model_name):
        time_start = time.time()
        maxbound = 999999
        minbound = -999999
        # region 定义模型
        model = Model()

        # region 定义决策变量
        alpha_uih = {}
        alpha_lih = {}
        beta_lik = {}
        beta_uik = {}
        ganma_ik = {}
        for i in range(params.V + 2):
            for h in range(params.H):
                name1 = "alpha_uij_{0:>2}_{1}_".format(i, h)
                name2 = "alpha_lij_{0:>2}_{1}_".format(i, h)
                alpha_uih[i, h] = model.continuous_var(0, params.c_ui[i], name=name1)
                alpha_lih[i, h] = model.continuous_var(0, params.c_li[i], name=name2)
        for i in range(params.V + 2):
            for k in range(params.K1):
                name3 = "beta_l{0}_{1}".format(i, k)
                name4 = "beta_u{0}_{1}".format(i, k)
                name5 = "ganma_{0}_{1}".format(i, k)
                beta_lik[i, k] = model.continuous_var(0, params.z, name=name3)
                beta_uik[i, k] = model.continuous_var(0, params.z, name=name4)
                ganma_ik[i, k] = model.continuous_var(lb=0,ub=1,name=name5)
        # 辅助决策变量用于线性化
        deta_u_i = {}
        deta_l_i = {}
        abs_ui1 = {}
        abs_li1 = {}
        sum_li1 = {}
        sum_ui1 = {}
        for i in range(1, params.V + 1):
            name6 = "deta_u_{0}".format(i)
            name7 = "deta_l_{0}".format(i)
            name8 = "abs_u_{0}".format(i)
            name9 = "abs_l_{0}".format(i)
            name10 = "sum_u_{0}".format(i)
            name11 = "sum_i_{0}".format(i)
            deta_u_i[i] = model.integer_var(0, maxbound, name=name6)
            deta_l_i[i] = model.integer_var(0, maxbound, name=name7)
            abs_ui1[i] = model.continuous_var(0, maxbound, name=name8)
            abs_li1[i] = model.continuous_var(0, maxbound, name=name9)
            sum_ui1[i] = model.integer_var(minbound, maxbound, name=name10)
            sum_li1[i] = model.integer_var(minbound, maxbound, name=name11)
        # endregion

        # region 定义目标函数
        obj_expr = model.linear_expr()  # 创建一个空的线性表达式对象
        for i in range(1, params.V + 1):
            expr1 = model.linear_expr()  # 创建一个空的线性表达式对象
            expr1.add_term(deta_l_i[i], params.time_p)
            expr1.add_term(deta_u_i[i], params.time_p)
            expr1.add_term(abs_li1[i], params.time_u)
            expr1.add_term(abs_ui1[i], params.time_u)
            expr2 = model.linear_expr()  # 创建一个空的线性表达式对象
            for k in range(params.K1):
                expr2.add_term(beta_uik[i, k], params.t_uki[k][i])
                expr2.add_term(beta_lik[i, k], params.t_lki[k][i])
            obj_expr.add(expr1)
            obj_expr.add(expr2)
            if params.add_cost:
                expr4 = model.sum(ganma_ik[i, k] * params.t_subblock for k in range(params.K1))
                obj_expr.add(expr4)
        model.minimize(obj_expr)
        # endregion

        # region 定义约束条件
        # 线性松弛后的补充约束：所有船分配到一个堆场里的数量不超过Z
        for k in range(params.K1):
            expr1 = model.sum(beta_uik[i,k] for i in range(1,params.V+1))
            expr2 = model.sum(beta_lik[i,k] for i in range(1,params.V+1))
            model.add(expr2+expr1 <=params.z,name ="constrain14_{}".format(k))
        # 线性松弛后的补充约束:所有船预留的堆场数量，最少 总数/z 向上取正
        for i in range(1,params.V+1):
            expr1 = model.sum(ganma_ik[i,k] for k in range(params.K1))
            model.add(expr1>= math.ceil((params.c_ui[i]+params.c_li[i])/params.z),name = "lower_container_num_{}".format(i))

        # 目标函数中的等价替换
        for i in range(1, params.V + 1):
            for h in range(params.H):
                model.add(deta_l_i[i] >= alpha_lih[i, h], name="linearing cons1_{0}_{1}".format(i, h))
                model.add(deta_u_i[i] >= alpha_uih[i, h], name="linearing cons2_{0}_{1}".format(i, h))
        for i in range(1, params.V + 1):
            expr3 = model.sum(beta_lik[i, k] for k in range(params.K0))
            expr4 = model.sum(beta_uik[i, k] for k in range(params.K0))
            model.add(sum_li1[i] == alpha_lih[i, 0] - expr3, name="Constrain_sum_l_{0}".format(i))
            model.add(sum_ui1[i] == alpha_uih[i, 0] - expr4, name="Constrain_sum_u_{0}".format(i))
        for i in range(1, params.V + 1):
            model.add(abs_li1[i] == model.abs(sum_li1[i]), name="abs_constrain_l_{0}".format(i))
            model.add(abs_ui1[i] == model.abs(sum_ui1[i]), name="abs_constrain_u_{0}".format(i))
        # 约束2/4：每艘船的unloading量守恒、loading量守恒
        if 1 == 1:
            for i in range(1, params.V + 1):
                expr1 = model.sum(alpha_uih[i, h] for h in range(params.H))
                expr2 = model.sum(alpha_lih[i, h] for h in range(params.H))
                model.add(expr1 == params.c_ui[i], name="Constrain2_u_{0}".format(i))
                model.add(expr2 == params.c_li[i], name="Constrain4_l_{0}".format(i))
        # 约束 3/5：每艘船unloading/loading到堆场的数量守恒
        if 1 == 1:
            for i in range(1, params.V + 1):
                expr3 = model.sum(beta_uik[i, k] for k in range(params.K1))
                expr4 = model.sum(beta_lik[i, k] for k in range(params.K1))
                model.add(expr3 == params.c_ui[i], name="Constrain2_u_{0}".format(i))
                model.add(expr4 == params.c_li[i], name="Constrain4_l_{0}".format(i))
        # 约束6：unloading 和 loading 在每一层的守恒
        if 1 == 1:
            for i in range(1, params.V + 1):
                for k in range(params.K1):
                    model.add(beta_lik[i, k] + beta_uik[i, k] <= params.z*ganma_ik[i,k], name="Constrain6_{0}_{1}".format(i, k))

        # 约束7-8：每个堆场的loading、unloading 量
        for i in range(1, params.V + 1):
            for k in range(params.K1):
                model.add(beta_lik[i, k] <= params.z * ganma_ik[i, k], name="Constrain7_l_{0}_{1}".format(i, k))
                model.add(beta_uik[i, k] <= params.z * ganma_ik[i, k], name="Constrain8_u_{0}_{1}".format(i, k))

        # 约束9：每层的subblock需要小于对应的params.r_ih
        for i in range(1, params.V + 1):
            expr1 = model.sum(ganma_ik[i, k] for k in range(params.K0))
            expr2 = model.sum(ganma_ik[i, k] for k in range(params.K0, params.K1))
            model.add(expr1 <= params.r_ih[i][0], name="Constrain9_0_{0}".format(i))
            model.add(expr2 <= params.r_ih[i][1], name="Constrain9_1_{0}".format(i))

        # 约束10：每个subblock只能分配给一个堆场
        if 1 == 1:
            for k in range(params.K1):
                expr = model.sum(ganma_ik[i, k] for i in range(1, params.V + 1))
                model.add(expr <= 1, name="Constrain10_{0}".format(k))
                # 约束11：每个time step，同一层的同一个block内，分配subblock数量不能超过3
        # if 1 == 1:
        #     for t in range(params.T):
        #         if params.total_handle[t] >= 1:
        #             for g in range(params.G):
        #                 expr = model.sum(params.l_it[i][t] * ganma_ik[i, k] for i in range(1, params.V + 1) for k in params.block_set[g])
        #                 model.add(expr <= 3, name="Constrain11_{0}_{1}".format(t, g))
        # if 1 == 1:
        #     for t in range(params.T):
        #         if params.total_handle[t] >= 1:
        #             for n in range(params.K1):
        #                 for k in range(1, len(params.neighbor_set[n])):  # 不包括自己
        #                     midlist = [n]
        #                     midlist.append(params.neighbor_set[n][k])
        #                     expr = model.linear_expr()
        #                     index_neighbor = n
        #                     for i in range(1, params.V + 1):
        #                         for index in midlist:  # --形成一个pair 对 等价于 for k in neighbor_pair
        #                             index_neighbor = index
        #                             expr.add_term(ganma_ik[i, index], params.l_it[i][t])
        #                     model.add(expr <= 1, name="Constrain12_{0}_({1}-{2})".format(t, n, index_neighbor))
        if 1 == 1:
            for t in range(params.T):
                expr = model.linear_expr()
                for i in range(1, params.V + 1):
                    if params.l_it[i][t] == 1:
                        expr.add_term(abs_ui1[i], 1 / params.handle_i[i])
                        expr.add_term(abs_li1[i], 1 / params.handle_i[i])
                model.add(expr <= params.C, name="Constrain13_{0}".format(t))
        # endregion

        solution = model.solve(log_output=False)
        time_end = time.time()
        num_variables = model.number_of_variables
        num_constraints = model.number_of_constraints
        refiner = ConflictRefiner()  # 先实例化ConflictRefiner类
        res = refiner.refine_conflict(model)  # 将模型导入该类，调用方法
        res.display()

        print("{} 中决策变量个数={}  约束数量={}".format(model_name,num_variables, num_constraints))
        if solution is not None:
            model_obj = solution.get_objective_value()
            self.solving_time_lb = round(time_end - time_start, 2)
            self.obj_lb = solution.get_objective_value()
            print("{2} 求解结果： obj = {0:.2f} s  转化为小时={1:.2f} h".format(model_obj, model_obj / 3600,model_name))
            # print("每艘船预留堆场的最优解情况-------------")
            cost_i = [0 for _ in range(params.V + 1)]
            for i in range(1, params.V + 1):
                cost1 = params.time_p * (max(solution.get_var_value(alpha_lih[i, 0]), solution.get_var_value(alpha_lih[i, 1])) +
                                         max(solution.get_var_value(alpha_uih[i, 0]), solution.get_var_value(alpha_uih[i, 0])))
                # unloading_value = 0
                # loading_Value = 0
                # for k in range(params.K0):
                #     unloading_value += solution.get_var_value(beta_uik[i, k])
                #     loading_Value += solution.get_var_value(beta_lik[i, k])
                # print("abs_ui1 ={}   公式计算的abs_ui1={}".format(abs_ui1[i].x,abs(alpha_uih[i, 0].x - unloading_value)))
                # cost2 = params.time_u * (abs(alpha_lih[i, 0].x - loading_Value) + abs(alpha_uih[i, 0].x - unloading_value))
                cost2222 = params.time_u * (solution.get_var_value(abs_ui1[i]) + solution.get_var_value(abs_li1[i]))
                # print("abs直接计算的结果={}   由原来的公式计算的结果={}".format(cost2222,cost2))
                cost3 = 0
                for k in range(params.K1):
                    cost3 += params.t_lki[k, i] * solution.get_var_value(beta_lik[i, k]) + params.t_uki[k, i] * solution.get_var_value(beta_uik[i, k])
                cost_i[i] = cost1 + cost2222 + cost3
                self.lb_cost_i[i] = cost_i[i]
            # for i in range(1, params.V + 1):
            #     LB_container =math.ceil((params.c_ui[i]+params.c_li[i])/params.z)
            #     print("vessel-{}  cost = {:.2f} abs_u={:.2f}  abs_i = {:.2f}   ganma_LB = {} 预留subblock情况= ".
            #           format(i, cost_i[i], solution.get_var_value(abs_ui1[i]), solution.get_var_value(abs_li1[i]),LB_container))
            #     for k in range(params.K1):
            #         if solution.get_var_value(ganma_ik[i, k]) > 0.00001:
            #             print("i={:3} k ={:3}  gamma_ik ={:.3f}  beta_uik={:5}  beta_lik={:5}".
            #                   format(i,k,solution.get_var_value(ganma_ik[i, k]),solution.get_var_value(beta_uik[i, k]),solution.get_var_value(beta_lik[i, k])))

            # for k in range(params.K1):
            #     for i in range(1,params.V+1):
            #         if  solution.get_var_value(beta_uik[i,k])>0 or   solution.get_var_value(beta_lik[i,k])>0 :
            #             print(" k = {:3} beta_u_{:2}={:5}   beta_l_{:2} = {:5}".format(k,i,solution.get_var_value(beta_uik[i,k]),i,solution.get_var_value(beta_lik[i,k])))
            # button_111 = 1
            # if button_111==1:
            #     stop_cmd = input("代码是否继续运行")

            #     print()
        else:
            print("docplex 无解!")
        model.clear()

    def build_test_model(self, model_name):
        '''
        半松弛原模型model---效果不好
        :param model_name:
        :return:
        '''
        time_start = time.time()
        maxbound = 999999
        minbound = -999999
        # region 定义模型
        model = Model()

        # region 定义决策变量
        alpha_uih = {}
        alpha_lih = {}
        beta_lik = {}
        beta_uik = {}
        ganma_ik = {}
        for i in range(params.V + 2):
            for h in range(params.H):
                name1 = "alpha_uij_{0:>2}_{1}_".format(i, h)
                name2 = "alpha_lij_{0:>2}_{1}_".format(i, h)
                alpha_uih[i, h] = model.integer_var(0, params.c_ui[i], name=name1)
                alpha_lih[i, h] = model.integer_var(0, params.c_li[i], name=name2)
        for i in range(params.V + 2):
            for k in range(params.K1):
                name3 = "beta_l{0}_{1}".format(i, k)
                name4 = "beta_u{0}_{1}".format(i, k)
                name5 = "ganma_{0}_{1}".format(i, k)
                beta_lik[i, k] = model.integer_var(0, params.z, name=name3)
                beta_uik[i, k] = model.integer_var(0, params.z, name=name4)
                # ganma_ik[i, k] = model.continuous_var(lb=0, ub=1, name=name5)
                ganma_ik[i, k] = model.binary_var(name=name5)
        # 辅助决策变量用于线性化
        deta_u_i = {}
        deta_l_i = {}
        abs_ui1 = {}
        abs_li1 = {}
        sum_li1 = {}
        sum_ui1 = {}
        for i in range(1, params.V + 1):
            name6 = "deta_u_{0}".format(i)
            name7 = "deta_l_{0}".format(i)
            name8 = "abs_u_{0}".format(i)
            name9 = "abs_l_{0}".format(i)
            name10 = "sum_u_{0}".format(i)
            name11 = "sum_i_{0}".format(i)
            deta_u_i[i] = model.integer_var(0, maxbound, name=name6)
            deta_l_i[i] = model.integer_var(0, maxbound, name=name7)
            abs_ui1[i] = model.integer_var(0, maxbound, name=name8)
            abs_li1[i] = model.integer_var(0, maxbound, name=name9)
            sum_ui1[i] = model.integer_var(minbound, maxbound, name=name10)
            sum_li1[i] = model.integer_var(minbound, maxbound, name=name11)
        # endregion

        # region 定义目标函数
        obj_expr = model.linear_expr()  # 创建一个空的线性表达式对象
        for i in range(1, params.V + 1):
            expr1 = model.linear_expr()  # 创建一个空的线性表达式对象
            expr1.add_term(deta_l_i[i], params.time_p)
            expr1.add_term(deta_u_i[i], params.time_p)
            expr1.add_term(abs_li1[i], params.time_u)
            expr1.add_term(abs_ui1[i], params.time_u)
            expr2 = model.linear_expr()  # 创建一个空的线性表达式对象
            for k in range(params.K1):
                expr2.add_term(beta_uik[i, k], params.t_uki[k][i])
                expr2.add_term(beta_lik[i, k], params.t_lki[k][i])
            obj_expr.add(expr1)
            obj_expr.add(expr2)
            if params.add_cost:
                expr4 = model.sum(ganma_ik[i, k] * params.t_subblock for k in range(params.K1))
                obj_expr.add(expr4)
        model.minimize(obj_expr)
        # endregion

        # region 定义约束条件
        # 线性松弛后的补充约束：所有船分配到一个堆场里的数量不超过Z
        for k in range(params.K1):
            expr1 = model.sum(beta_uik[i, k] for i in range(1, params.V + 1))
            expr2 = model.sum(beta_lik[i, k] for i in range(1, params.V + 1))
            model.add(expr2 + expr1 <= params.z, name="constrain14_{}".format(k))

        # 目标函数中的等价替换
        for i in range(1, params.V + 1):
            for h in range(params.H):
                model.add(deta_l_i[i] >= alpha_lih[i, h], name="linearing cons1_{0}_{1}".format(i, h))
                model.add(deta_u_i[i] >= alpha_uih[i, h], name="linearing cons2_{0}_{1}".format(i, h))
        for i in range(1, params.V + 1):
            expr3 = model.sum(beta_lik[i, k] for k in range(params.K0))
            expr4 = model.sum(beta_uik[i, k] for k in range(params.K0))
            model.add(sum_li1[i] == alpha_lih[i, 0] - expr3, name="Constrain_sum_l_{0}".format(i))
            model.add(sum_ui1[i] == alpha_uih[i, 0] - expr4, name="Constrain_sum_u_{0}".format(i))
        for i in range(1, params.V + 1):
            model.add(abs_li1[i] == model.abs(sum_li1[i]), name="abs_constrain_l_{0}".format(i))
            model.add(abs_ui1[i] == model.abs(sum_ui1[i]), name="abs_constrain_u_{0}".format(i))
        # 约束2/4：每艘船的unloading量守恒、loading量守恒
        if 1 == 1:
            for i in range(1, params.V + 1):
                expr1 = model.sum(alpha_uih[i, h] for h in range(params.H))
                expr2 = model.sum(alpha_lih[i, h] for h in range(params.H))
                model.add(expr1 == params.c_ui[i], name="Constrain2_u_{0}".format(i))
                model.add(expr2 == params.c_li[i], name="Constrain4_l_{0}".format(i))
        # 约束 3/5：每艘船unloading/loading到堆场的数量守恒
        if 1 == 1:
            for i in range(1, params.V + 1):
                expr3 = model.sum(beta_uik[i, k] for k in range(params.K1))
                expr4 = model.sum(beta_lik[i, k] for k in range(params.K1))
                model.add(expr3 == params.c_ui[i], name="Constrain2_u_{0}".format(i))
                model.add(expr4 == params.c_li[i], name="Constrain4_l_{0}".format(i))
        # 约束6：unloading 和 loading 在每一层的守恒
        if 1 == 1:
            for i in range(1, params.V + 1):
                for k in range(params.K1):
                    model.add(beta_lik[i, k] + beta_uik[i, k] <= params.z*ganma_ik[i,k], name="Constrain6_{0}_{1}".format(i, k))

        # # 约束9：每层的subblock需要小于对应的params.r_ih
        for i in range(1, params.V + 1):
            expr1 = model.sum(ganma_ik[i, k] for k in range(params.K0))
            expr2 = model.sum(ganma_ik[i, k] for k in range(params.K0, params.K1))
            model.add(expr1 <= params.r_ih[i][0], name="Constrain9_0_{0}".format(i))
            model.add(expr2 <= params.r_ih[i][1], name="Constrain9_1_{0}".format(i))
        #
        # # 约束10：每个subblock只能分配给一个堆场
        for k in range(params.K1):
            expr = model.sum(ganma_ik[i, k] for i in range(1, params.V + 1))
            model.add(expr <= params.V, name="Constrain10_{0}".format(k))

        # 约束11：每个time step，同一层的同一个block内，分配subblock数量不能超过3
        # if 1 == 1:
        #     for t in range(params.T):
        #         if params.total_handle[t] >= 1:
        #             for g in range(params.G):
        #                 expr = model.sum(params.l_it[i][t] * ganma_ik[i, k] for i in range(1, params.V + 1) for k in params.block_set[g])
        #                 model.add(expr <= 3, name="Constrain11_{0}_{1}".format(t, g))
        # if 1 == 1:
        #     for t in range(params.T):
        #         if params.total_handle[t] >= 1:
        #             for n in range(params.K1):
        #                 for k in range(1, len(params.neighbor_set[n])):  # 不包括自己
        #                     midlist = [n]
        #                     midlist.append(params.neighbor_set[n][k])
        #                     expr = model.linear_expr()
        #                     index_neighbor = n
        #                     for i in range(1, params.V + 1):
        #                         for index in midlist:  # --形成一个pair 对 等价于 for k in neighbor_pair
        #                             index_neighbor = index
        #                             expr.add_term(ganma_ik[i, index], params.l_it[i][t])
        #                     model.add(expr <= 1, name="Constrain12_{0}_({1}-{2})".format(t, n, index_neighbor))
        # if 1 == 1:
        #     for t in range(params.T):
        #         expr = model.linear_expr()
        #         for i in range(1, params.V + 1):
        #             if params.l_it[i][t] == 1:
        #                 expr.add_term(abs_ui1[i], 1 / params.handle_i[i])
        #                 expr.add_term(abs_li1[i], 1 / params.handle_i[i])
        #         model.add(expr <= params.C, name="Constrain13_{0}".format(t))
        # endregion

        solution = model.solve(log_output=True)
        time_end = time.time()
        num_variables = model.number_of_variables
        num_constraints = model.number_of_constraints
        refiner = ConflictRefiner()  # 先实例化ConflictRefiner类
        res = refiner.refine_conflict(model)  # 将模型导入该类，调用方法
        res.display()

        print("{} 中决策变量个数={}  约束数量={}".format(model_name, num_variables, num_constraints))
        if solution is not None:
            model_obj = solution.get_objective_value()
            self.solving_time = round(time_end - time_start, 2)
            self.obj = solution.get_objective_value()
            print("{2} 求解结果： obj = {0:.2f} s  转化为小时={1:.2f} h    耗时：{3:.2f}".format(model_obj, model_obj / 3600, model_name,self.solving_time))
            # print("每艘船预留堆场的最优解情况-------------")
            cost_i = [0 for _ in range(params.V + 1)]
            for i in range(1, params.V + 1):
                cost1 = params.time_p * (max(solution.get_var_value(alpha_lih[i, 0]), solution.get_var_value(alpha_lih[i, 1])) +
                                         max(solution.get_var_value(alpha_uih[i, 0]), solution.get_var_value(alpha_uih[i, 0])))
                # unloading_value = 0
                # loading_Value = 0
                # for k in range(params.K0):
                #     unloading_value += solution.get_var_value(beta_uik[i, k])
                #     loading_Value += solution.get_var_value(beta_lik[i, k])
                # print("abs_ui1 ={}   公式计算的abs_ui1={}".format(abs_ui1[i].x,abs(alpha_uih[i, 0].x - unloading_value)))
                # cost2 = params.time_u * (abs(alpha_lih[i, 0].x - loading_Value) + abs(alpha_uih[i, 0].x - unloading_value))
                cost2222 = params.time_u * (solution.get_var_value(abs_ui1[i]) + solution.get_var_value(abs_li1[i]))
                # print("abs直接计算的结果={}   由原来的公式计算的结果={}".format(cost2222,cost2))
                cost3 = 0
                for k in range(params.K1):
                    cost3 += params.t_lki[k, i] * solution.get_var_value(beta_lik[i, k]) + params.t_uki[k, i] * solution.get_var_value(beta_uik[i, k])
                cost_i[i] = cost1 + cost2222 + cost3
                self.lb_cost_i[i] = cost_i[i]
            for i in range(1, params.V + 1):
                print("vessel-{}  cost = {:.2f} abs_u={:.2f}  abs_i = {:.2f}  预留subblock = ".
                      format(i, cost_i[i], solution.get_var_value(abs_ui1[i]), solution.get_var_value(abs_li1[i])), end=" ")
                for k in range(params.K1):
                    if solution.get_var_value(ganma_ik[i, k]) > 0.5:
                        print(f"{k:2} ", end="")
                print()
            # for k in range(params.K1):
            #     for i in range(1, params.V + 1):
            #         if solution.get_var_value(beta_uik[i, k]) > 0 or solution.get_var_value(beta_lik[i, k]) > 0:
            #             print(" k = {:3} beta_u_{:2}={:5}   beta_l_{:2} = {:5}".format(k, i, solution.get_var_value(beta_uik[i, k]), i, solution.get_var_value(beta_lik[i, k])))
            button_111 = 1
            if button_111 == 1:
                stop_cmd = input("代码是否继续运行")

            #     print()
        else:
            print("docplex 无解!")

    def build_compare_model(self,model_name,problem_scale = 3):
        time_start = time.time()
        maxbound = 999999
        minbound = -999999
        # region 定义模型
        model = Model()
        if problem_scale <= 0:
            model.set_time_limit(params.test_running_limitation)  # 测试规模最多求300s
        elif problem_scale <= 1:
            limit_time = rd.randint(1200, 2400)
            model.set_time_limit(limit_time)  # 测试1/2问题规模最多求1200-2400s
        else:
            model.set_time_limit(3600)  # 正式规模最多求3600s
        # region 定义决策变量
        alpha_uih = {}
        alpha_lih = {}
        beta_lik = {}
        beta_uik = {}
        ganma_ik = {}
        for i in range(params.V + 2):
            for h in range(params.H):
                name1 = "alpha_uij_{0:>2}_{1}_".format(i, h)
                name2 = "alpha_lij_{0:>2}_{1}_".format(i, h)
                alpha_uih[i, h] = model.integer_var(0, params.c_ui[i], name=name1)
                alpha_lih[i, h] = model.integer_var(0, params.c_li[i], name=name2)
        for i in range(params.V + 2):
            for k in range(params.K1):
                name3 = "beta_l{0}_{1}".format(i, k)
                name4 = "beta_u{0}_{1}".format(i, k)
                name5 = "ganma_{0}_{1}".format(i, k)
                beta_lik[i, k] = model.integer_var(0, params.z, name=name3)
                beta_uik[i, k] = model.integer_var(0, params.z, name=name4)
                ganma_ik[i, k] = model.binary_var(name=name5)

        # 辅助决策变量用于线性化
        deta_u_i = {}
        deta_l_i = {}
        for i in range(1, params.V + 1):
            name6 = "deta_u_{0}".format(i)
            name7 = "deta_l_{0}".format(i)
            name8 = "abs_u_{0}".format(i)
            name9 = "abs_l_{0}".format(i)
            name10 = "sum_u_{0}".format(i)
            name11 = "sum_i_{0}".format(i)
            deta_u_i[i] = model.integer_var(0, maxbound, name=name6)
            deta_l_i[i] = model.integer_var(0, maxbound, name=name7)
        # endregion

        # region 定义目标函数
        obj_expr = model.linear_expr()  # 创建一个空的线性表达式对象
        for i in range(1, params.V + 1):
            expr1 = model.linear_expr()  # 创建一个空的线性表达式对象
            expr1.add_term(deta_l_i[i], params.time_p)
            expr1.add_term(deta_u_i[i], params.time_p)
            expr2 = model.sum(beta_lik[i, k] * params.t_lki[k, i] for k in range(params.K1))
            expr3 = model.sum(beta_uik[i, k] * params.t_uki[k, i] for k in range(params.K1))
            obj_expr.add(expr1)
            obj_expr.add(expr2)
            obj_expr.add(expr3)
            if params.add_cost:
                expr4 = model.sum(ganma_ik[i, k] * params.t_subblock for k in range(params.K1))
                obj_expr.add(expr4)
        model.minimize(obj_expr)
        # endregion

        # region 定义约束条件
        # 目标函数中的等价替换
        for i in range(1, params.V + 1):
            for h in range(params.H):
                model.add(deta_l_i[i] >= alpha_lih[i, h], name="linearing cons1_{0}_{1}".format(i, h))
                model.add(deta_u_i[i] >= alpha_uih[i, h], name="linearing cons2_{0}_{1}".format(i, h))
        # 不考虑转运的约束1：
        for i in range(1,params.V+1):
            for h in range(params.H):
                model.add(alpha_lih[i,h]+alpha_uih[i,h]<=params.r_ih[i,h]*params.z,name="compare_constr1_{}_{}".format(i,h))
        # 不考虑转运的约束2:
        for i in range(1,params.V+1):
            expr1 = model.sum(beta_lik[i,k] for k in range(params.K0))
            expr2 = model.sum(beta_lik[i,k] for k in range(params.K0,params.K1))
            expr3 = model.sum(beta_uik[i, k] for k in range(params.K0))
            expr4 = model.sum(beta_uik[i, k] for k in range(params.K0, params.K1))
            model.add(expr1 == alpha_lih[i, 0], name="compare_constr2_h1_loading_{}".format(i))
            model.add(expr2 == alpha_lih[i, 1], name="compare_constr2__h2_loading_{}".format(i))
            model.add(expr3 == alpha_uih[i, 0], name="compare_constr2_h1_loading_{}".format(i))
            model.add(expr4 == alpha_uih[i, 1], name="compare_constr2__h2_loading_{}".format(i))


        # 约束2/4：每艘船的unloading量守恒、loading量守恒
        if 1 == 1:
            for i in range(1, params.V + 1):
                expr1 = model.sum(alpha_uih[i, h] for h in range(params.H))
                expr2 = model.sum(alpha_lih[i, h] for h in range(params.H))
                model.add(expr1 == params.c_ui[i], name="Constrain2_u_{0}".format(i))
                model.add(expr2 == params.c_li[i], name="Constrain4_l_{0}".format(i))

        # 约束6：unloading 和 loading 在每一层的上界
        if 1 == 1:
            for i in range(1, params.V + 1):
                for k in range(params.K1):
                    model.add(beta_lik[i, k] + beta_uik[i, k] <= params.z * ganma_ik[i, k], name="Constrain6_{0}_{1}".format(i, k))

        # 约束9：每层的subblock需要小于对应的params.r_ih
        for i in range(1, params.V + 1):
            expr1 = model.sum(ganma_ik[i, k] for k in range(params.K0))
            expr2 = model.sum(ganma_ik[i, k] for k in range(params.K0, params.K1))
            model.add(expr1 <= params.r_ih[i][0], name="Constrain9_0_{0}".format(i))
            model.add(expr2 <= params.r_ih[i][1], name="Constrain9_1_{0}".format(i))

        # 约束10：每个subblock只能分配给一个堆场
        if 1 == 1:
            for k in range(params.K1):
                expr = model.sum(ganma_ik[i, k] for i in range(1, params.V + 1))
                model.add(expr <= 1, name="Constrain10_{0}".format(k))
        # 约束11：每个time step，同一层的同一个block内，分配subblock数量不能超过3
        if 1 == 1:
            for t in range(params.T):
                if params.total_handle[t] >= 1:
                    for g in range(params.G):
                        expr = model.sum(params.l_it[i][t] * ganma_ik[i, k] for i in range(1, params.V + 1) for k in params.block_set[g])
                        model.add(expr <= 3, name="Constrain11_{0}_{1}".format(t, g))
        if 1 == 1:
            for t in range(params.T):
                if params.total_handle[t] >= 1:
                    for n in range(params.K1):
                        for k in range(1, len(params.neighbor_set[n])):  # 不包括自己
                            midlist = [n]
                            midlist.append(params.neighbor_set[n][k])
                            expr = model.linear_expr()
                            index_neighbor = n
                            for i in range(1, params.V + 1):
                                for index in midlist:  # --形成一个pair 对 等价于 for k in neighbor_pair
                                    index_neighbor = index
                                    expr.add_term(ganma_ik[i, index], params.l_it[i][t])
                            model.add(expr <= 1, name="Constrain12_{0}_({1}-{2})".format(t, n, index_neighbor))
        # endregion

        solution = model.solve(log_output=True)
        time_end = time.time()
        num_variables = model.number_of_variables
        num_constraints = model.number_of_constraints
        refiner = ConflictRefiner()  # 先实例化ConflictRefiner类
        res = refiner.refine_conflict(model)  # 将模型导入该类，调用方法
        res.display()

        print("{} 中决策变量个数={}  约束数量={}".format(model_name, num_variables, num_constraints))
        if solution is not None:
            model_obj = solution.get_objective_value()
            self.solving_time_compare = round(time_end - time_start, 2)
            self.obj_compare = solution.get_objective_value()

            cost_i = [0 for _ in range(params.V + 1)]
            cost_t = 0
            cost_p = 0
            cost_u = 0
            for i in range(1, params.V + 1):
                cost1 = params.time_p * (max(solution.get_var_value(alpha_lih[i, 0]), solution.get_var_value(alpha_lih[i, 1])) +
                                         max(solution.get_var_value(alpha_uih[i, 0]), solution.get_var_value(alpha_uih[i, 1])))
                # unloading_value = 0
                # loading_Value = 0
                # for k in range(params.K0):
                #     unloading_value += solution.get_var_value(beta_uik[i, k])
                #     loading_Value += solution.get_var_value(beta_lik[i, k])
                # print("abs_ui1 ={}   公式计算的abs_ui1={}".format(abs_ui1[i].x,abs(alpha_uih[i, 0].x - unloading_value)))
                # cost2 = params.time_u * (abs(alpha_lih[i, 0].x - loading_Value) + abs(alpha_uih[i, 0].x - unloading_value))
                # print("abs直接计算的结果={}   由原来的公式计算的结果={}".format(cost2222,cost2))
                cost3 = 0
                for k in range(params.K1):
                    cost3 += params.t_lki[k, i] * solution.get_var_value(beta_lik[i, k]) + params.t_uki[k, i] * solution.get_var_value(beta_uik[i, k])
                cost_i[i] = cost1 + cost3
                cost_t+=cost3
                cost_p+=cost1
            # print("每艘船预留堆场的最优解情况-------------")
            # for i in range(1, params.V+ 1):
            #     print("vessel-{}  cost = {:.2f} abs_u={:.2f}  abs_i = {:.2f}  预留subblock = ".
            #           format(i,cost_i[i],solution.get_var_value(abs_ui1[i]),solution.get_var_value(abs_li1[i])), end=" ")
            #     for k in range(params.K1):
            #         if solution.get_var_value(ganma_ik[i, k])> 0.5:
            #             print(f"{k:2} ", end="")
            #     print()
            self.compare_output.append(cost_t)
            self.compare_output.append(cost_p)
            self.compare_output.append(cost_u)
            print("{2} 求解结果： obj = {0:.2f} s  转化为小时={1:.2f} h  sum(vessel)={3:.2f}".format(model_obj, model_obj / 3600, model_name, sum(cost_i)))

        else:
            print("docplex 无解!")
